//
//  CTVCache.m
//  Channltv
//
// <singleton>
//
//  Created by Bruno  Keymolen on 23/07/07.
//  Copyright 2007 channl.tv. All rights reserved.
//

#import "CTVCache.h"
#import "CTVConfig.h"
#import "CTVCrypto.h"
#import "CTVHTTPDataDownload.h"

@implementation CTVCache

+(id)GetInstance {
	static id _CTVSingleton = nil;
	if (_CTVSingleton == nil) {
		_CTVSingleton = [self new];
		[_CTVSingleton setup];
	}	
    return _CTVSingleton;
}

-(void)setup{
	m_listeners = [[NSMutableArray alloc] init];
	m_mediaDownloads = [[NSMutableDictionary alloc] init];
	m_resourceDownloads = [[NSMutableDictionary alloc] init];
	m_dataDownloads = [[NSMutableDictionary alloc] init];
}
 
 
-(void)_informListenersUrlCacheFinished:(NSString*)urlString andStatusCode:(int)status{
	NSEnumerator* listeners = [m_listeners objectEnumerator];
	CTVCacheListener* listener;
	while(listener = [listeners nextObject]){
		[listener cacheFinishedForURL:urlString andStatusCode:status];
	}
}
 

//In: String
//Out: String; Hexadecimal representation of the hash 
+(NSString*)getHashString:(NSString*)urlString{
	NSData *din, *dout;
	char buf[255];
	char* pout;
	din = [urlString dataUsingEncoding: NSASCIIStringEncoding];
	dout = [CTVCrypto md5Digest:din];
	pout = (char*)[[dout description] cStringUsingEncoding:NSASCIIStringEncoding];
	//create hex representation in string
	int tel, pos;
	pos = 0;
	for(tel=0;tel<[[dout description] length];tel++){
		char thisChar = pout[tel]; 
		if( (thisChar >= 'a' && thisChar <='f') ||
		    (thisChar >= 'A' && thisChar <='F') ||
			(thisChar >= '0' && thisChar <='9') )
		{
			buf[pos] = thisChar;
			pos++;
		}			  
	}
	buf[pos] = 0; //trailing 0
	return [NSString stringWithCString:buf encoding:NSASCIIStringEncoding];
}

+(NSString*)getCachePathForURL:(NSString*)urlString{
	NSString* ret;

	//Add path
	ret = [[CTVConfig GetInstance] tmpdir]; 
	ret = [ret stringByAppendingString:[CTVCache getHashString:urlString]];
		
	return ret;					
}

+(NSString*)getDataCachePathForURL:(NSString*)urlString{
	NSString* ret;

	//Add path
	ret = [[CTVConfig GetInstance] dataCacheDir]; 
	ret = [ret stringByAppendingString:[CTVCache getHashString:urlString]];
		
	return ret;					
}

+(NSString*)getMediaCachePathForURL:(NSString*)urlString{
	NSString* ret;

	//Add path
	ret = [[CTVConfig GetInstance] mediaCacheDir]; 
	ret = [ret stringByAppendingString:[CTVCache getHashString:urlString]];
		
	return ret;					
}

+(NSString*)getResourceCachePathForURL:(NSString*)urlString{
	NSString* ret;

	//Add path

	ret = [[CTVConfig GetInstance] resourceCacheDir]; 
	ret = [ret stringByAppendingString:[CTVCache getHashString:urlString]];
		
	return ret;					
}




//Search for the file /..tmpdir../[hexrepres].<EXT>
+(NSString*)getMediaCacheEntryForURL:(NSString*)urlString{
	//get all file that start with "path"
	NSString* fileHash = [CTVCache getHashString:urlString];
	NSFileManager* fileManager = [NSFileManager defaultManager];
	NSString* cachePath = [[CTVConfig GetInstance] mediaCacheDir]; 
	
	NSArray* fileNames = [fileManager directoryContentsAtPath:cachePath];
	
	NSEnumerator *enumerator = [fileNames objectEnumerator];
	NSString* fName;
	while (fName = [enumerator nextObject]) {
		if([fName hasPrefix:fileHash]){
			NSString* ret = [cachePath stringByAppendingString:fName];
			return ret;
		}
	}
	return nil;
}


+(BRBitmapTexture*)getStreamTextureForScene:(BRRenderScene*)scene{
	static BRBitmapTexture* __ctv_streamTexture = nil;
	
	if(__ctv_streamTexture == nil){
		NSString* streamPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"stream" ofType: @"png"];
		__ctv_streamTexture = [CTVCache createBitmapTextureFromPath:streamPath withScene:scene];
		[__ctv_streamTexture retain];
	}

	return __ctv_streamTexture;
}


+(BRBitmapTexture*)getOnDiskTextureForScene:(BRRenderScene*)scene{	
	static BRBitmapTexture* __ctv_onDiskTexture = nil;
	
	if(__ctv_onDiskTexture == nil){
		NSString* streamPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"ondisk" ofType: @"png"];
		__ctv_onDiskTexture = [CTVCache createBitmapTextureFromPath:streamPath withScene:scene];
		[__ctv_onDiskTexture retain];
	}

	return __ctv_onDiskTexture;
}

+(BRBitmapTexture*)getWatchPointTextureForScene:(BRRenderScene*)scene{
	static BRBitmapTexture* __ctv_watchPointTexture = nil;
	
	if(__ctv_watchPointTexture == nil){
		NSString* streamPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"watchpoint" ofType: @"png"];
		__ctv_watchPointTexture = [CTVCache createBitmapTextureFromPath:streamPath withScene:scene];
		[__ctv_watchPointTexture retain];
	}

	return __ctv_watchPointTexture;
}

+(BRBitmapTexture*)getBigRateTextureForScene:(BRRenderScene*)scene andRate:(float)rate{
	return [CTVCache _getBigRateTextureForScene:scene andRate:rate andPrefix:@"lrate"];
}

+(BRBitmapTexture*)getRateTextureForScene:(BRRenderScene*)scene andRate:(float)rate{
	return [CTVCache _getRateTextureForScene:scene andRate:rate andPrefix:@"rate"];
}

+(BRBitmapTexture*)_getRateTextureForScene:(BRRenderScene*)scene andRate:(float)rate andPrefix:(NSString*)prefix{
	static BRBitmapTexture* __ctv_rateTexture0_0 = nil;
	static BRBitmapTexture* __ctv_rateTexture0_5 = nil;
	static BRBitmapTexture* __ctv_rateTexture1_0 = nil;
	static BRBitmapTexture* __ctv_rateTexture1_5 = nil;
	static BRBitmapTexture* __ctv_rateTexture2_0 = nil;
	static BRBitmapTexture* __ctv_rateTexture2_5 = nil;
	static BRBitmapTexture* __ctv_rateTexture3_0 = nil;
	static BRBitmapTexture* __ctv_rateTexture3_5 = nil;
	static BRBitmapTexture* __ctv_rateTexture4_0 = nil;
	static BRBitmapTexture* __ctv_rateTexture4_5 = nil;
	static BRBitmapTexture* __ctv_rateTexture5_0 = nil;
		
		
	if(rate == 0.0f && __ctv_rateTexture0_0 != nil){
		return __ctv_rateTexture0_0;
	}
	if(rate == 0.5f && __ctv_rateTexture0_5 != nil){
		return __ctv_rateTexture0_5;
	}
	if(rate == 1.0f && __ctv_rateTexture1_0 != nil){
		return __ctv_rateTexture1_0;
	}
	if(rate == 1.5f && __ctv_rateTexture1_5 != nil){
		return __ctv_rateTexture1_5;
	}
	if(rate == 2.0f && __ctv_rateTexture2_0 != nil){
		return __ctv_rateTexture2_0;
	}
	if(rate == 2.5f && __ctv_rateTexture2_5 != nil){
		return __ctv_rateTexture2_5;
	}
	if(rate == 3.0f && __ctv_rateTexture3_0 != nil){
		return __ctv_rateTexture3_0;
	}
	if(rate == 3.5f && __ctv_rateTexture3_5 != nil){
		return __ctv_rateTexture3_5;
	}
	if(rate == 4.0f && __ctv_rateTexture4_0 != nil){
		return __ctv_rateTexture4_0;
	}
	if(rate == 4.5f && __ctv_rateTexture4_5 != nil){
		return __ctv_rateTexture4_5;
	}	
	if(rate == 5.0f && __ctv_rateTexture5_0 != nil){
		return __ctv_rateTexture5_0;
	}
	
	//Rate not in cache, get it
	int big = rate;
	int low = (rate * 10.0f) - (big * 10); 
	
	NSString* rateIcon = [NSString stringWithFormat:@"%@%d_%d", prefix, big, low];
	NSString* ratePath = [[NSBundle bundleForClass:[self class]] pathForResource:rateIcon ofType: @"png"];
	
	if(rate == 0.0f){
		__ctv_rateTexture0_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture0_0 retain];
		return __ctv_rateTexture0_0;
	}
	if(rate == 0.5f){
		__ctv_rateTexture0_5 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture0_5 retain];
		return __ctv_rateTexture0_5;
	}
	if(rate == 1.0f){
		__ctv_rateTexture1_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture1_0 retain];
		return __ctv_rateTexture1_0;
	}
	if(rate == 1.5f){
		__ctv_rateTexture1_5 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture1_5 retain];
		return __ctv_rateTexture1_5;
	}
	if(rate == 2.0f){
		__ctv_rateTexture2_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture2_0 retain];
		return __ctv_rateTexture2_0;
	}
	if(rate == 2.5f){
		__ctv_rateTexture2_5 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture2_5 retain];
		return __ctv_rateTexture2_5;
	}
	if(rate == 3.0f){
		__ctv_rateTexture3_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture3_0 retain];
		return __ctv_rateTexture3_0;
	}
	if(rate == 3.5f){
		__ctv_rateTexture3_5 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture3_5 retain];
		return __ctv_rateTexture3_5;
	}
	if(rate == 4.0f){
		__ctv_rateTexture4_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture4_0 retain];
		return __ctv_rateTexture4_0;
	}
	if(rate == 4.5f){
		__ctv_rateTexture4_5 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture4_5 retain];
		return __ctv_rateTexture4_5;
	}
	if(rate == 5.0f){
		__ctv_rateTexture5_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__ctv_rateTexture5_0 retain];
		return __ctv_rateTexture5_0;
	}

	return nil;
}


+(BRBitmapTexture*)_getBigRateTextureForScene:(BRRenderScene*)scene andRate:(float)rate andPrefix:(NSString*)prefix{
	static BRBitmapTexture* __l_ctv_rateTexture0_0 = nil;
	static BRBitmapTexture* __l_ctv_rateTexture1_0 = nil;
	static BRBitmapTexture* __l_ctv_rateTexture2_0 = nil;
	static BRBitmapTexture* __l_ctv_rateTexture3_0 = nil;
	static BRBitmapTexture* __l_ctv_rateTexture4_0 = nil;
	static BRBitmapTexture* __l_ctv_rateTexture5_0 = nil;
		
		
	if(rate == 0.0f && __l_ctv_rateTexture0_0 != nil){
		return __l_ctv_rateTexture0_0;
	}
	if(rate == 1.0f && __l_ctv_rateTexture1_0 != nil){
		return __l_ctv_rateTexture1_0;
	}
	if(rate == 2.0f && __l_ctv_rateTexture2_0 != nil){
		return __l_ctv_rateTexture2_0;
	}
	if(rate == 3.0f && __l_ctv_rateTexture3_0 != nil){
		return __l_ctv_rateTexture3_0;
	}
	if(rate == 4.0f && __l_ctv_rateTexture4_0 != nil){
		return __l_ctv_rateTexture4_0;
	}
	if(rate == 5.0f && __l_ctv_rateTexture5_0 != nil){
		return __l_ctv_rateTexture5_0;
	}
	
	//Rate not in cache, get it
	int big = rate;
	int low = (rate * 10.0f) - (big * 10); 
	
	NSString* rateIcon = [NSString stringWithFormat:@"%@%d_%d", prefix, big, low];
	NSString* ratePath = [[NSBundle bundleForClass:[self class]] pathForResource:rateIcon ofType: @"png"];
	
	if(rate == 0.0f){
		__l_ctv_rateTexture0_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__l_ctv_rateTexture0_0 retain];
		return __l_ctv_rateTexture0_0;
	}
	if(rate == 1.0f){
		__l_ctv_rateTexture1_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__l_ctv_rateTexture1_0 retain];
		return __l_ctv_rateTexture1_0;
	}
	if(rate == 2.0f){
		__l_ctv_rateTexture2_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__l_ctv_rateTexture2_0 retain];
		return __l_ctv_rateTexture2_0;
	}
	if(rate == 3.0f){
		__l_ctv_rateTexture3_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__l_ctv_rateTexture3_0 retain];
		return __l_ctv_rateTexture3_0;
	}
	if(rate == 4.0f){
		__l_ctv_rateTexture4_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__l_ctv_rateTexture4_0 retain];
		return __l_ctv_rateTexture4_0;
	}
	if(rate == 5.0f){
		__l_ctv_rateTexture5_0 = [CTVCache createBitmapTextureFromPath:ratePath withScene:scene];
		[__l_ctv_rateTexture5_0 retain];
		return __l_ctv_rateTexture5_0;
	}

	return nil;
}



+(BRBitmapTexture*)createBitmapTextureFromPath:(NSString*)path withScene:(BRRenderScene*)scene{
	NSURL* url = nil;
	CIImage* image = nil;
	BRBitmapTexture * texture = nil;

	if( [[NSFileManager defaultManager] fileExistsAtPath:path] ){
			url = [NSURL fileURLWithPath:path];			
		//Create img from URL
		image = [CIImage imageWithContentsOfURL:url];  

		//Create texture 
		BRRenderContext * ctx = [scene resourceContext];
		texture = [BRBitmapTexture textureWithCIImage: image
												context: ctx
												 mipmap: YES];
	}

	return texture;
}



+(BOOL)urlExist:(NSString*)urlString{
	NSString* cachePathString = [CTVCache getCachePathForURL:urlString];
NSLog(@"urlExist?:%s", [cachePathString UTF8String]);	
	return [[NSFileManager defaultManager] fileExistsAtPath:cachePathString];
}

-(CGImageRef)CreateImageForURLString:(NSString*)urlString{
	CGImageRef img;
	NSString* cachePathString;
	if([urlString hasPrefix:@"http"]){
		cachePathString = [CTVCache getResourceCachePathForURL:urlString];	
	}else{
		cachePathString = urlString;
	}
	NSURL* url = [NSURL fileURLWithPath:cachePathString];
	img = (CGImageRef)CreateImageForURL(url);
	return img;	
}


-(void)clearCache{
	//DEPRECATED!
	NSString* syscmd = [NSString stringWithFormat:@"rm -f %s*", [[[CTVConfig GetInstance] tmpdir] UTF8String]];
	system([syscmd UTF8String]);
}


-(void)addCacheListener:(CTVCacheListener*)listener{
	[m_listeners addObject:listener];
}


-(void)removeCacheListener:(CTVCacheListener*)listener{
	[m_listeners removeObject:listener];
}




//Return nil when no download is in progress
-(CTVHTTPMediaDownload*)isDownloadingURLString:(NSString*)url{
	return [m_mediaDownloads objectForKey:url];
}




// Called when Media cache is fnished
// CTVMediaDownloadReply
-(int)finishMediaDownload:(CTVMediaDownload*)mediaDownload{
	[m_mediaDownloads removeObjectForKey:[mediaDownload urlString]];
	
	//Inform our listeners that a cache process is finished for a URL
	[self _informListenersUrlCacheFinished:[mediaDownload urlString] andStatusCode:[mediaDownload httpStatusCode] ];
		
	return 0;
}

-(int)releaseMediaDownload:(CTVMediaDownload*)mediaDownload{

	//Release the Downloader Object
	[mediaDownload release];

	return 0;
}


//Called when Resource cache is fnished
//CTVResourceDownloadReply
-(int)finishResourceDownload:(CTVHTTPResourceDownload*)resourceDownload{
	[m_resourceDownloads removeObjectForKey:[resourceDownload urlString]];

	//Inform our listeners that a cache process is finished for a URL
	[self _informListenersUrlCacheFinished:[resourceDownload urlString] andStatusCode:[resourceDownload httpStatusCode]];

	return 0;
}

-(int)releaseResourceDownload:(CTVHTTPResourceDownload*)resourceDownload{
	
	//Release the Downloader Object
	[resourceDownload release];
	
	return 0;
}


//Called when Data cache is fnished
//CTVDataDownloadReply
-(int)finishDataDownload:(CTVHTTPDataDownload*)dataDownload{
	[m_dataDownloads removeObjectForKey:[dataDownload urlString]];
	
	//Inform our listeners that a cache process is finished for a URL
	[self _informListenersUrlCacheFinished:[dataDownload urlString] andStatusCode:[dataDownload httpStatusCode]];
	
	return 0;
}

-(int)releaseDataDownload:(CTVHTTPDataDownload*)dataDownload{

	//Release the Downloader Object
	[dataDownload release];
	
	return 0;
}



-(BOOL)isMediaInCacheForURLString:(NSString*)url{
	if([CTVCache getMediaCacheEntryForURL:url]){
		return TRUE;
	}
	return FALSE;
}





//Asynchronous and always overwrite old file's
//Return data path
-(NSString*)cacheData:(NSString*)urlstring{
	NSString* ret = nil;
	
	CTVHTTPDataDownload* down = [self cacheDataURLString:urlstring cachePath:&ret];
	
	if(down != nil){
		ret = [down cachePath]; 
	}else{
		//download error or is already in cache 
	}
	
	return ret;
}



//Asynchronous
//Return data path (releated to cacheURLString)
-(NSString*)cacheMedia:(NSString*)url andClip:(CTVClip*)clip{
	NSString* ret = nil;
	
	CTVHTTPMediaDownload* down = [self cacheURLString:url cachePathNoExt:&ret andClip:clip];
	
	if(down != nil){
		ret = [down cacheEntryNoExtension]; 
	}
	
	return ret;
}


//Asynchronous
//Return data path 
-(NSString*)cacheResource:(NSString*)url{
	NSString* ret = nil;
	
	CTVHTTPResourceDownload* down = [self cacheResourceURLString:url cachePath:&ret];
	
	if(down != nil){
		ret = [down cachePath]; 
	}else{
		//download error or is already in cache 
	}
	
	return ret;
}



-(CTVHTTPResourceDownload*)cacheResourceURLString:(NSString*)urlstring cachePath:(NSString**)cachePath{
	int ret;
	CTVHTTPResourceDownload* download;
	
	download = [m_resourceDownloads objectForKey:urlstring];
	//Download is not busy, start a new one
	if(download == nil){
		download = [[CTVHTTPResourceDownload alloc] initWithURLString:urlstring andResourceReply:(CTVResourceDownloadReply*)self];
		ret = [download cache];
		//1=already in cache, 0=downloading, -1=error
		if(ret < 0){
			//error
			[download release];
			download = nil;
			[self _informListenersUrlCacheFinished:urlstring andStatusCode:ret];
		}else if(ret == 1){
			*cachePath = [NSString stringWithString:[download cachePath]];
			[download release];
			download = nil;					
			[self _informListenersUrlCacheFinished:urlstring andStatusCode:ret];
		}else{ //ret == 0
			*cachePath = [NSString stringWithString:[download cachePath]];
			[m_resourceDownloads setValue:download forKey:urlstring];
		}
	}else{
		*cachePath = [NSString stringWithString:[download cachePath]];
	}
	
	return download;
}



-(CTVHTTPDataDownload*)cacheDataURLString:(NSString*)urlstring cachePath:(NSString**)cachePath{
	int ret;
	CTVHTTPDataDownload* download;
	
	download = [m_dataDownloads objectForKey:urlstring];
	//Download is not busy, start a new one
	if(download == nil){
		download = [[CTVHTTPDataDownload alloc] initWithURLString:urlstring andDataReply:(CTVDataDownloadReply*)self];
		ret = [download cache];
		//1=already in cache, 0=downloading, -1=error
		if(ret < 0){
			//error
			[download release];
			download = nil;
			[self _informListenersUrlCacheFinished:urlstring andStatusCode:ret];
		}else if(ret == 1){
			*cachePath = [NSString stringWithString:[download cachePath]];
			[download release];
			download = nil;					
			[self _informListenersUrlCacheFinished:urlstring andStatusCode:ret];
		}else{ //ret == 0
			*cachePath = [NSString stringWithString:[download cachePath]];
			[m_dataDownloads setValue:download forKey:urlstring];
		}
	}else{
		*cachePath = [NSString stringWithString:[download cachePath]];
	}
	
	return download;
}



-(CTVHTTPMediaDownload*)cacheURLString:(NSString*)url cachePathNoExt:(NSString**)cachePath andClip:(CTVClip*)clip{
	int ret;
	CTVHTTPMediaDownload* download;
	
	download = [m_mediaDownloads objectForKey:url];
	
	//Download is not busy, start a new one
	if(download == nil){
		download = [[CTVHTTPMediaDownload alloc] initWithURLString:url andClip:clip andMediaReply:(CTVMediaDownloadReply*)self];
		ret = [download startDownload];
		//1=already in cache, 0=downloading, -1=error
		if(ret < 0){
			[download release];
			download = nil;
			*cachePath = nil;
			[self _informListenersUrlCacheFinished:url andStatusCode:ret];
		}else if(ret >0){
			*cachePath = [NSString stringWithString:[download cacheEntryNoExtension]];		
			[download release];
			download = nil;
			[self _informListenersUrlCacheFinished:url andStatusCode:ret];
		}else{
			*cachePath = [NSString stringWithString:[download cacheEntryNoExtension]];		
			[m_mediaDownloads setValue:download forKey:url];
		}
	}else{
		*cachePath = [NSString stringWithString:[download cacheEntryNoExtension]];		
	}
	
	return download;
}


-(int)saveWatchPoint:(CTVChannel*)channel{
	NSString* buf;
	NSString* fileName;
	
	 
	//Create Cache File name
	fileName = [NSString stringWithFormat:@"%@%@.watchpoint", [[CTVConfig GetInstance] dataCacheDir], [channel channelID]];
	//Create watchpoint buffer
	buf = [NSString stringWithFormat:@"%f;%@%C", [[channel watchPoint] offset], [[channel watchPoint] clipID], 0]; //Set 0 at the end (use %C instead of %c to insert a 16bit character)
	
	NSLog(@"WRITE file:%@ :%@", fileName, buf);

	//(over)write buffer to file
	[buf writeToFile:fileName atomically:TRUE];
	
	return 0;
}

-(int)loadWatchPoint:(CTVChannel*)channel{
	NSString* fileName;
	char* buf;
	float offset;
	NSString* clipID;
	
	offset = 0.0f;
	clipID = nil;
	
	//Create Cache File name
	fileName = [NSString stringWithFormat:@"%@%@.watchpoint", [[CTVConfig GetInstance] dataCacheDir], [channel channelID]];

	if( [[NSFileManager defaultManager] fileExistsAtPath:fileName] ){

		//read buffer from file
		NSData* data = [NSData dataWithContentsOfFile:fileName];
		buf = (char*)[data bytes];
		
	
		
		//Set clip id and offset
		char* semiColon = strchr(buf, ';');
		if(semiColon != NULL){
			clipID = [NSString stringWithCString:(semiColon+1)];
			*semiColon = 0;
			offset = atof(buf);
			[[channel watchPoint] setOffset:offset];
	NSLog(@"Cache, setClipID:%@ forChannel:%@", clipID, [channel name] );
			[[channel watchPoint] setClipID:clipID];
		}else{
			return -2;
		}
	
		
		return 0;
	}
	
	
	return -1;
}



@end



